package com.google.code.mochaccino.framework.freemarker;

/*
 * Copyright 2012 Claude Houle claude.houle@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.google.code.mochaccino.framework.freemarker.exception.TemplateLoadingException;
import com.google.code.mochaccino.framework.freemarker.exception.TemplateNotFoundException;
import com.google.code.mochaccino.framework.freemarker.exception.TemplateRenderingException;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import org.apache.commons.io.IOUtils;

/** Template Manager */
public class TemplateManager {

	private Configuration      cfg               = null;
	private Map<String,Object> defaultParameters = new HashMap<String,Object>();

	/** Constructor */
	public TemplateManager( TemplateLoader loader ) {
		this( loader, DefaultObjectWrapper.getDefaultInstance() );
	}

	/** Constructor for DI */
	@Inject
	public TemplateManager( TemplateLoader loader, TemplateConfiguration defaultConfiguration ) {
		this( loader, DefaultObjectWrapper.getDefaultInstance(), defaultConfiguration );
	}

	/** Constructor */
	public TemplateManager( TemplateLoader loader, ObjectWrapper wrapper ) {
		this( loader, wrapper, null );
	}

	/** Constructor */
	public TemplateManager( TemplateLoader loader, ObjectWrapper wrapper, TemplateConfiguration defaultConfiguration ) {
		cfg = new Configuration();
		cfg.setTemplateLoader( loader );
		cfg.setObjectWrapper( wrapper );
		setDefaultConfiguration( defaultConfiguration );
	}

	/** @return the defaultParameters */
	public Map<String,Object> getDefaultParameters() {
		return defaultParameters;
	}

	/** @param parameters the defaultParameters to set */
	public void setDefaultParameters( Map<String,Object> parameters ) {
		this.defaultParameters = parameters;
	}

	/** Process Reports */
	public String processTemplate( String name, Map<String,Object> parameters ) throws TemplateNotFoundException, TemplateLoadingException, TemplateRenderingException {
		if ( name == null ) {
			throw new IllegalArgumentException( "Template Base Name cannot be null!" );
		}
		StringWriter out = new StringWriter();
		processTemplate( name, parameters, out, null );
		return out.toString();
	}

	/** Process Report */
	public void processTemplate( String name, Map<String,Object> parameters, File out ) throws TemplateNotFoundException, TemplateLoadingException, TemplateRenderingException {
		if ( name == null ) {
			throw new IllegalArgumentException( "Template Base Name cannot be null!" );
		}
		if ( out == null ) {
			throw new IllegalArgumentException( "Output Stream/Writer cannot be null!" );
		}
		try {
			processTemplate( name, parameters, new BufferedWriter( new FileWriter( out ) ) );
		} catch ( IOException e ) {
			throw new TemplateRenderingException( e );
		}
	}

	/** Process Report */
	public void processTemplate( String name, Map<String,Object> parameters, Writer out ) throws TemplateNotFoundException, TemplateLoadingException, TemplateRenderingException {
		processTemplate( name, parameters, out, null );
	}

	/** Process Report */
	public void processTemplate( String name, Map<String,Object> parameters, OutputStream out ) throws TemplateNotFoundException, TemplateLoadingException, TemplateRenderingException {
		if ( name == null ) {
			throw new IllegalArgumentException( "Template Base Name cannot be null!" );
		}
		if ( out == null ) {
			throw new IllegalArgumentException( "Output Stream/Writer cannot be null!" );
		}
		processTemplate( name, parameters, out, null );
	}

	/** Process Report */
	public void processTemplate( String name, Map<String,Object> parameters, OutputStream out, TemplateConfiguration conf ) throws TemplateNotFoundException, TemplateLoadingException, TemplateRenderingException {
		if ( name == null ) {
			throw new IllegalArgumentException( "Template Base Name cannot be null!" );
		}
		if ( out == null ) {
			throw new IllegalArgumentException( "Output Stream/Writer cannot be null!" );
		}
		processTemplate( name, parameters, new OutputStreamWriter( out ), conf );
	}

	/** Process Report */
	public void processTemplate( String name, Map<String,Object> parameters, Writer out, TemplateConfiguration conf ) throws TemplateNotFoundException, TemplateLoadingException, TemplateRenderingException {
		if ( name == null ) {
			throw new IllegalArgumentException( "Template Base Name cannot be null!" );
		}
		if ( out == null ) {
			throw new IllegalArgumentException( "Output Stream/Writer cannot be null!" );
		}

		/*
		 * Fetch the Template
		 */
		String templateName = name;
		Template template = null;
		try {
			template = cfg.getTemplate( templateName );
		} catch ( IOException e1 ) {
			throw new TemplateLoadingException( e1 );
		}
		if ( template == null ) {
			throw new TemplateNotFoundException( "Template named '" + templateName + "' could not be found!" );
		}

		/*If specified parameters map is null, then create a one*/
		if ( parameters == null ) {
			parameters = new HashMap<String,Object>();
		}

		/*
		 * Add Default Parameters to the supplied parameters. Any user-specified parameters
		 * that presents a name conflict with default parameters will be automatically overriden.
		 */
		if ( defaultParameters != null ) {
			parameters.putAll( defaultParameters );
		}

		/*Customize Template with specified template configuration*/
		if ( conf != null ) {
			conf.customize( template );
		}
		try {
			template.process( parameters, out );
		} catch ( IOException e ) {
			throw new TemplateRenderingException( e );
		} catch ( TemplateException e ) {
			throw new TemplateRenderingException( e );
		}
		IOUtils.closeQuietly( out );
	}

	/** Default Configuration */
	public void setDefaultConfiguration( TemplateConfiguration conf ) {
		if ( conf != null ) {
			conf.customize( cfg );
		}
	}

	public boolean templateExist( String templateName ) {
		try {
			Template tmpl = cfg.getTemplate( templateName );
			if ( tmpl == null ) {
				return false;
			}
			return true;
		} catch ( IOException e ) {
			return false;
		}
	}
}
